package venus

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gitee.com/dark.H/go-remote-repl/cli"
	log "github.com/sirupsen/logrus"

	"gitee.com/dark.H/go-remote-repl/remote"
)

var (
	UseFileSave = false
	DefaultDir  = filepath.Join(os.TempDir(), "CachedReq")
	IfOk        = os.Mkdir(DefaultDir, os.ModePerm)
)

type DataBase struct {
	data    map[string]Status
	channel chan struct {
		url string
		re  interface{}
		t   int
	}
}

func (status *Status) String() string {
	return fmt.Sprintf("%s :: [%d] :: %s ", status.Url, status.Status, status.Method)
}

func (d *DataBase) Add(url string, req *http.Request) {
	lock.Lock()
	defer lock.Unlock()
	if UseFileSave {
		defer func() {

			// name := base64.StdEncoding.EncodeToString([]byte(url))
			// name := strings.ReplaceAll(url, ":", "_")
			// name = strings.ReplaceAll(name, "/", "-")
			// name = strings.ReplaceAll(name, "?", "_")
			// name = strings.ReplaceAll(name, "#", "_")
			// name = strings.ReplaceAll(name, "&", "_")
			// name = strings.ReplaceAll(name, "~", "_")
			// name = strings.ReplaceAll(name, "%", "_")
			// name = strings.ReplaceAll(name, "=", "_")
			// name = strings.ReplaceAll(name, " ", "_")
			e := md5.Sum([]byte(url))
			name := hex.EncodeToString(e[:])

			name = req.Method + "__" + name
			name = filepath.Join(DefaultDir, name)
			fp, err := os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_RDWR, os.ModePerm)
			if err != nil {
				log.Error(err)
				return
			}
			defer fp.Close()
			fp.Write([]byte("\n---------- Req Start ----------\n" + d.data[url].Req))

		}()
	}

	body := ""
	// var buffer bytes.Buffer
	// req.Body = ioutil.NopCloser(io.TeeReader(req.Body, &buffer))
	// buf, err := ioutil.ReadAll(req.Body)
	if req == nil {
		return
	}
	if req.Body == nil {
		d.data[url] = Status{
			Url:    url,
			Req:    ReqString(req),
			Method: req.Method,
			Time:   time.Now(),
		}
	} else {
		// bodyReader, err := req.GetBody()
		buf, err := ioutil.ReadAll(req.Body)
		if err == nil {
			body = string(buf)
			req.Body = ioutil.NopCloser(bytes.NewReader(buf))
			// log.Info("put int :", body)
		} else {
			log.Fatal(err)
		}
		d.data[url] = Status{
			Url:    url,
			Method: req.Method,
			Req:    ReqString(req) + body,
			Time:   time.Now(),
		}

	}
}

func (d *DataBase) Fin(url string, res *http.Response) {
	lock.Lock()
	defer lock.Unlock()
	if UseFileSave {
		defer func() {
			// name := base64.StdEncoding.EncodeToString([]byte(url))
			// // name := strings.ReplaceAll(url, ":", "_")
			// // name = strings.ReplaceAll(name, "/", "-")
			// // name = strings.ReplaceAll(name, "?", "_")
			// // name = strings.ReplaceAll(name, "#", "_")
			// // name = strings.ReplaceAll(name, "&", "_")
			// // name = strings.ReplaceAll(name, "~", "_")
			// name = strings.ReplaceAll(name, "%", "_")
			// // name = strings.ReplaceAll(name, "=", "_")
			// // name = strings.ReplaceAll(name, " ", "_")
			e := md5.Sum([]byte(url))
			name := hex.EncodeToString(e[:])

			name = d.data[url].Method + "__" + name
			name = filepath.Join(DefaultDir, name)
			fp, err := os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_RDWR, os.ModePerm)
			if err != nil {
				log.Error(err)
				return
			}
			defer fp.Close()
			fp.Write([]byte("\n------------------------------\n"))
			fp.Write([]byte(d.data[url].Res + "\n------------------------------\n"))
		}()
	}

	c := d.data[url]
	c.UsedTime = time.Now().Sub(d.data[url].Time)
	// if reader, err := ResBody(res); err != nil {
	// c.Res = ResString(res)
	// } else {
	// if buf, err := ioutil.ReadAll(reader); err == nil {
	// c.Res = ResString(res) + string(buf)
	// } else {
	c.Res = ResString(res)
	// }
	// }
	c.Status = res.StatusCode
	d.data[url] = c

}

func (d *DataBase) Ls() (o []string) {
	for _, v := range d.data {
		o = append(o, v.String())
	}
	return
}

func (d *DataBase) Get(u string) string {
	return "\n-------------------- req header start -------------------->\n" + d.data[u].Req + "\n-------------------- sep --------------------\n" + d.data[u].Res + "\n-------------------- one request END --------------------\n"
}

func (d *DataBase) RunInBack() {
	delete(remote.RegistedFunc, "sess")
	delete(remote.RegistedFunc, "db in memory")
	delete(remote.RegistedFunc, "file sync")

	remote.RegistedFunc["links"] = func(m *remote.ApiServerMan) {
		for {
			os := d.Ls()
			if one, err := m.Choose("Which Links you want to See:", os...); err == nil {
				u := strings.TrimSpace(strings.Split(one, "::")[0])

				m.SendMsg(strings.ReplaceAll(d.Get(u), "\r", ""))
			} else {
				break
			}
		}
	}
	go func() {
		for c := range d.channel {
			switch c.t {
			case 0:
				d.Fin(c.url, c.re.(*http.Response))
			case 1:
				d.Add(c.url, c.re.(*http.Request))
			}
		}
	}()

	cli.StartOuterAPIServer()
	// }()

}

func NewData() *DataBase {
	return &DataBase{
		data: make(map[string]Status),
		channel: make(chan struct {
			url string
			re  interface{}
			t   int
		}),
	}
}

func (d *DataBase) Put(url string, r interface{}, tp int) {
	d.channel <- struct {
		url string
		re  interface{}
		t   int
	}{
		url: url,
		re:  r,
		t:   tp,
	}

}
